using System;
using System.Collections.Generic;
using System.Text;
using System.ComponentModel;
using NUnit.Framework;

namespace Clarius.PresentationModel.Design.Tests
{
	[TestFixture]
	public class SupportInitializeFixture
	{
		private static SupportInitialize CreateInstance()
		{
			return new SupportInitialize();
		}

		[Test]
		public void ShouldInitializeObject()
		{
			object obj = CreateInstance();
			ISupportInitialize init = (ISupportInitialize)obj;
			ISupportInitializeNotification notify = (ISupportInitializeNotification)obj;

			Assert.IsFalse(notify.IsInitialized);

			bool called = false;
			notify.Initialized += delegate { called = true; };

			init.BeginInit();
			init.EndInit();

			Assert.IsTrue(called);
			Assert.IsTrue(notify.IsInitialized);
		}

		[Test]
		public void ShouldIsChangedStartFalse()
		{
			object obj = CreateInstance();
			IChangeTracking change = (IChangeTracking)obj;

			Assert.IsFalse(change.IsChanged);
		}

		[Test]
		public void ShouldIsChangedTrueOnPropertySet()
		{
			SupportInitialize obj = new SupportInitialize();
			IChangeTracking change = (IChangeTracking)obj;

			obj.MyProperty = 5;

			Assert.IsTrue(change.IsChanged);
		}

		[Test]
		public void ShouldIsChangedFalseAfterInitializeNoErrors()
		{
			SupportInitialize obj = new SupportInitialize();
			IChangeTracking change = (IChangeTracking)obj;

			obj.MyProperty = 5;

			obj.Initialize();

			Assert.IsFalse(change.IsChanged);
		}

		[Test]
		public void ShouldIsChangedTrueIfNestedChanged()
		{
			SupportInitialize obj = new SupportInitialize();
			IChangeTracking change = (IChangeTracking)obj;

			Assert.IsFalse(change.IsChanged);

			obj.NestedSupport = new SupportInitialize();

			Assert.IsTrue(change.IsChanged);

			obj.Initialize();

			Assert.IsFalse(change.IsChanged);

			((SupportInitialize)obj.NestedSupport).MyProperty = 5;

			Assert.IsTrue(change.IsChanged);
		}

		[Test]
		public void ShouldIsInitializedFalseIfChildNotInitialized()
		{
			SupportInitialize obj = new SupportInitialize();
			ISupportInitialize init = (ISupportInitialize)obj;
			ISupportInitializeNotification notify = (ISupportInitializeNotification)obj;

			init.BeginInit();
			init.EndInit();

			Assert.IsTrue(notify.IsInitialized);

			obj.NestedSupport = new SupportInitialize();

			Assert.IsFalse(notify.IsInitialized);

			ISupportInitialize childInit = (ISupportInitialize)obj.NestedSupport;
			childInit.BeginInit();
			childInit.EndInit();

			Assert.IsTrue(notify.IsInitialized);
		}

		[ExpectedException(typeof(InvalidOperationException))]
		[Test]
		public void ShouldThrowIfEndInitCalledBeforeBeginInit()
		{
			ISupportInitialize init = (ISupportInitialize)CreateInstance();

			init.EndInit();
		}

		[Test]
		public void ShouldRaiseTypedPropertyChanged()
		{
			SupportInitialize obj = new SupportInitialize();
			bool called = false;
			obj.MyPropertyChanged += delegate { called = true; };

			obj.MyProperty = 5;

			Assert.IsTrue(called);
		}

		[Test]
		public void ShouldRaiseGenericPropertyChanged()
		{
			SupportInitialize obj = new SupportInitialize();
			bool called = false;
			obj.PropertyChanged += delegate { called = true; };

			obj.MyProperty = 5;

			Assert.IsTrue(called);
		}

		[Test]
		public void ShouldInitializeCallDoValidate()
		{
			SupportInitialize obj = CreateInstance();

			obj.Initialize();

			Assert.AreEqual(1, obj.InitializeCalls);
		}

		[Test]
		public void ShouldCallDoValidateAgainOnlyIfChanged()
		{
			SupportInitialize obj = CreateInstance();

			obj.Initialize();
			obj.Initialize();
			obj.Initialize();

			Assert.AreEqual(1, obj.InitializeCalls);

			obj.MyProperty = 5;

			obj.Initialize();
			obj.Initialize();
			obj.Initialize();

			Assert.AreEqual(2, obj.InitializeCalls);
		}

		[ExpectedException(typeof(InvalidOperationException))]
		[Test]
		public void ShouldEnsureInitializedThrow()
		{
			SupportInitialize obj = CreateInstance();

			obj.Ensure();
		}

		[Test]
		public void ShouldReportNestedChangedProperties()
		{
			DerivedSupport derived = new DerivedSupport();
			derived.DerivedValue = 5;

			Assert.IsTrue(((IChangeTracking)derived).IsChanged);

			derived.Initialize();
			Assert.IsFalse(((IChangeTracking)derived).IsChanged);

			derived.Value = new SupportInitialize();
			Assert.IsTrue(((IChangeTracking)derived).IsChanged);

			derived.Initialize();
			Assert.IsFalse(((IChangeTracking)derived).IsChanged);

			((SupportInitialize)derived.Value).MyProperty = 10;
			Assert.IsTrue(((IChangeTracking)derived).IsChanged);
		}

	}

	public partial class SupportInitialize
	{
		public int ValidateCalls;
		public int InitializeCalls;

		public void Ensure()
		{
			EnsureInitialized();
		}

		private int myVar;

		public int MyProperty
		{
			get { return myVar; }
			set { myVar = value; RaiseMyPropertyChanged(); }
		}

		private object nested;

		public object NestedSupport
		{
			get { return nested; }
			set { nested = value; RaiseNestedSupportChanged(); }
		}

		protected virtual void DoValidate()
		{
			ValidateCalls++;
		}

		protected virtual void DoInitialize()
		{
			InitializeCalls++;
		}
	}

	public partial class DerivedSupport : SupportInitialize
	{
		private int derivedValue;

		public int DerivedValue
		{
			get { return derivedValue; }
			set { derivedValue = value; RaiseDerivedValueChanged(); }
		}

		private object value;

		public object Value
		{
			get { return value; }
			set { this.value = value; RaiseValueChanged(); }
		}

		protected override bool HasNestedChanges
		{
			get
			{
				return base.HasNestedChanges || IsValueChanged(value as IChangeTracking);
			}
		}

		protected override bool AreNestedInitialized
		{
			get
			{
				return base.AreNestedInitialized && IsValueInitialized(value as ISupportInitializeNotification);
			}
		}

		protected override void InitializeNested()
		{
			base.InitializeNested();
			ISupportInitialize initializable;

			initializable = (ISupportInitialize)value;
			if (initializable != null)
			{
				initializable.BeginInit();
				initializable.EndInit();
			}
		}

		protected override void DoValidate()
		{
			base.DoValidate();
		}

		protected override void DoInitialize()
		{
			base.DoInitialize();
		}
	}

	partial class DerivedSupport
	{
		public event EventHandler DerivedValueChanged;

		private void RaiseDerivedValueChanged()
		{
			if (DerivedValueChanged != null)
				DerivedValueChanged(this, EventArgs.Empty);

			RaisePropertyChanged("DerivedValue");
		}

		public event EventHandler ValueChanged;

		private void RaiseValueChanged()
		{
			if (ValueChanged != null)
				ValueChanged(this, EventArgs.Empty);

			RaisePropertyChanged("Value");
		}


	}
}
